package com.ruoyi.web.controller.common;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.awt.desktop.AboutHandler;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * 单点登录控制器
 * 仅作为学习使用，正式项目需加强验证
 * 单点实现思路 A单点进入B系统
 * 1.A请求B获取进入B系统的入场券
 * 2.A根据入场券与用户信息，再次请求B系统，
 * @author liuzx
 */
@RestController
@RequestMapping("/authV2")
public class SingleLoginControllerV2 {

    @Resource
    private RedisCache redisCache;

    /**
     * 密钥
     */
    public static final String APP_SECRET = "123456";


/* ========================================================= A 端 ============================================================================ */

    /**
     * A端生成单点license，后续需要通过license来查询获取到用户信息，并返回给B端
     * @param appId 应用id
     * @return
     */
    @GetMapping("/generateLicense")
    public AjaxResult generateLicense(String appId) {
        if(StringUtils.isEmpty(appId)) {
            return AjaxResult.error("appId不能为空");
        }
        // 根据appId查询对应的应用，然后获取登录地址，同时将license和登录地址返回给前端，前端再进行跳转外链
        // 这里模拟就不做查询
        LoginUser user = SecurityUtils.getLoginUser();
        String timeStamp = String.valueOf(System.currentTimeMillis());
        String license = Md5Utils.createLicense(appId, user.getUsername(), APP_SECRET, timeStamp);
        // 将许可证作为key，用户信息作为值存入redis，有效时间3分钟
        redisCache.setCacheObject(license,user,180, TimeUnit.SECONDS);
        HashMap<String,Object> map = new HashMap<>();
        map.put("license",license);
        map.put("timeStamp",timeStamp);
        map.put("redirectUrl","/authV2/getUserInfo");
        return AjaxResult.success(map);
    }

    /**
     * 获取用户基础信息并返回
     * @param appId 应用id
     * @param license 登录许可证
     * @param timeStamp 时间戳
     * @param sign 签名
     * @return 返回用户信息，真实环境用户信息需加密返回
     */
    @GetMapping("/getUserInfo")
    public AjaxResult getUserInfo(String appId, String license,String timeStamp,String sign) {
        if(StringUtils.isEmpty(appId)) {
            return AjaxResult.error("appId不能为空");
        }
        if(StringUtils.isEmpty(license)) {
            return AjaxResult.error("登录许可证不能为空");
        }
        if(StringUtils.isEmpty(timeStamp)) {
            return AjaxResult.error("回调地址不能为空");
        }
        if(StringUtils.isEmpty(sign)) {
            return AjaxResult.error("签名不能为空");
        }
        // 计算时间戳与当前时间差值，大于三分钟直接返回
        long timeStampNow = System.currentTimeMillis();
        if((timeStampNow - Long.parseLong(timeStamp))/ 1000 > 180 || !redisCache.hasKey(license)) {
            return AjaxResult.error("许可已失效，请重新获取");
        }
        LoginUser user = redisCache.getCacheObject(license);
        // redis获取后立即删除，防止恶意攻击多次访问
        redisCache.deleteObject(license);
        if(user == null) {
            return AjaxResult.error("用户信息不存在");
        }
        if(!Md5Utils.checkLicense(license,appId,user.getUsername(),APP_SECRET,timeStamp)) {
            return AjaxResult.error("签名验证失败");
        }
        HashMap<String,Object> map = new HashMap<>();
        map.put("username",user.getUsername());
        map.put("phone",user.getUser().getPhonenumber());
        map.put("nickname",user.getUser().getNickName());
        return AjaxResult.success(map);
    }
    /* ========================================================= B 端 ============================================================================ */
    /**
     * A端单点到B端  第一步是点击B端开放的一个接口，并传入一定的参数
     * @param license 登录许可证(A端提供）
     * @param timestamp 时间戳(A端提供）
     * @param redirectUrl 回调地址，A端获取用户信息的地址，将前面参数再放进去，同时塞入签名和时间戳用作校验
     * @return
     */
    @GetMapping("/login")
    public AjaxResult login(String license, String timestamp, String redirectUrl) {
        if(StringUtils.isEmpty(license)) {
            return AjaxResult.error("登录许可证不能为空");
        }
        if(StringUtils.isEmpty(redirectUrl)) {
            return AjaxResult.error("回调地址不能为空");
        }
        HashMap<String,Object> map = new HashMap<>();
        map.put("license",license);
        map.put("timestamp",timestamp);
        // 根据回调地址获取用户信息
        String response = HttpUtil.get(redirectUrl,map);
        if(StringUtils.isEmpty(response)) {
            return AjaxResult.error("请求失败");
        }
        JSONObject jsonObject = JSON.parseObject(response);
        if(200 == jsonObject.getIntValue("code")) {
            // 获取到用户数据 模拟登录 返回token
            JSONObject data = JSON.parseObject(jsonObject.getString("data"));

        } else {
            return AjaxResult.error(jsonObject.getString("msg"));
        }
        // 模拟登录
        return AjaxResult.success();
    }

}
